本文原理对大部分 Android 应用适用,直接进入主题。

运行环境

  • 方案一:Android 手机已取得 Root 权限,并成功安装 Xposed 插件,大部分国产机的系统目前对获取权限的支持都很不友好,高版本系统安装 Xposed 还可能造成系统变软砖等问题,仅适用爱折腾的同学。
  • 方案二:在手机安装 VirtualXposed,这里是它的下载页面,类似一个运行在 Android 上的开源虚拟机,可以把手机内的程序拖到该虚拟机里,就可以使用插件啦,推荐第二种方式。

提示:1.Android 9.0 暂不支持上面两种方案。2.类似的 VirtualXposed 的软件还有太极等。

Xposed简介

Xposed框架(Xposed framework)是一套开放源代码的、在Android高权限模式下运行的框架服务,可以在不修改APK文件的情况下修改程序的运行(修改系统),简单的说,就是通过反射找到我们需要 Hook 的类、方法、属性并修改它,事实上我们几乎可以做所有事,本例我们就是利用插件修改应用运行时获取位置信息的方法,实现任意位置打卡(虽然我们并没有迟到)。
至于 Xposed 的原理,是替换 /system/bin/app_precesss 程序控制 zygote 进程,使得它在系统启动的过程中会加载 Xposed framework 的 XposedBridge.jar 文件(也就是每个进程开启都被会被添加一些神秘代码),应用自然就可以被劫持了。

定位原理

应用一般可通过多种方式进行定位,如 GPS 定位、WIFI 定位、基站定位、AGPS 定位,而且通常会混合同时使用,我们使用 Xposed 开发中,可以让应用调用一些获取位置的方法失效,而引导到我们希望应用调用的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//基站定位相关
android.telephony.TelephonyManager
getCellLocation()
getAllCellInfo()
android.telephony.PhoneStateListener
onCellLocationChanged(cellLocation)
onCellInfoChanged()
//WIFI定位相关
android.net.wifi.WifiManager
getScanResults()
getWifiState()
isWifiEnabled()
getMacAddress()
getSSID()
getBSSID()
//获取网络状态
android.net.NetworkInfo
isConnectedOrConnecting()
isConnected()
isAvailable()
//下面是关键的,要修改经纬度坐标的方法
android.location.LocationManager
getLastLocation()
getLastKnownLocation(string)
getProviders()
getBestProvider(criteria, boolean)
requestLocationUpdates(...)
requestSingleUpdate(...)

首先让基站定位和 WIFI 定位的方法都返回失败,让应用继续去请求其他定位方式,然后通过修改 providers 等方法返回值,最终引导应用去请求 GPS 位置,最后我们只要伪造一个 Location 返回给应用调用就可以啦。
具体代码请移步 GitHub 仓库,修改这些方法中调用的经纬度坐标,我们就成功把应用程序 “欺骗” 了。
然后只要写一个页面接入地图,把经纬度坐标传到我们的插件就大功告成了。

遇到的问题

笔者使用腾讯地图 API 但是发现坐标系和企业微信里的有一些不一致,所以做了一定调整适配它,并且在每次调用定位方法时做加盐处理。更具体的实现可以参考代码实现。
VirtualXposed 能满足日常使用,但偶尔会出现不稳定情况,可能和机型也有关,原生 Root 则很稳定。

仓库地址

weworkhook